#include "svmTrain.h"
SVMTrain::SVMTrain(const char * _svmModelFile, const char * _svmConfigureFile, const char * _svmPositiveFeaturePath, const char * _svmNegetiveFeaturePath):svmModelFile(_svmModelFile),svmConfigureFile(_svmConfigureFile),svmPositiveFeaturePath(_svmPositiveFeaturePath),svmNegetiveFeaturePath(_svmNegetiveFeaturePath)
{
  model = NULL ;
  nodeSpace = NULL ;
  readConfigureFile(svmConfigureFile.c_str(),param) ; // 读入配置文件
  INFO1("svm configure file read finish") ; 
}
SVMTrain::~SVMTrain()
{
  svm_destroy_param(&param) ;

  delete[] prob.y ; // 释放标识空间
  delete[] prob.x ; // 释放样本的指针空间
  delete[] nodeSpace ; // 释放实际样本的内存空间
  if( model != NULL )
    svm_free_and_destroy_model(&model) ; // 释放model内存
}

void SVMTrain::readConfigureFile(const char * _fileName, svm_parameter&_param) 
{

  if( _fileName == NULL ) 
    {
      ERROR("configre file name shouldn't null!") ;
      exit(-1) ;
    }
  // default values
  _param.svm_type = C_SVC;
  //enum { LINEAR, POLY, RBF, SIGMOID, PRECOMPUTED }; /* kernel_type */
  _param.kernel_type = RBF;
  _param.degree = 3;
  _param.gamma = 0;	// 1/num_features
  _param.coef0 = 0;
  _param.nu = 0.5;
  _param.cache_size = 100;
  _param.C = 1;
  _param.eps = 1e-3;
  _param.p = 0.1;
  _param.shrinking = 1;
  _param.probability = 0; // 预测概率
  _param.nr_weight = 0;
  _param.weight_label = NULL;
  _param.weight = NULL;
  
  std::map<std::string,std::string> configureMap ;
  initContentConfigure(_fileName,configureMap);
  if( configureMap.find(std::string("svm_type")) != configureMap.end())
    _param.svm_type = atoi(configureMap[std::string("svm_type")].c_str()) ;
  if( configureMap.find(std::string("kernel_type")) != configureMap.end())
    _param.kernel_type = atoi(configureMap[std::string("kernel_type")].c_str()) ;
  if( configureMap.find(std::string("degree")) != configureMap.end())
    _param.degree = atoi(configureMap[std::string("degree")].c_str()) ;
  if( configureMap.find(std::string("gamma")) != configureMap.end())
    _param.gamma = atof(configureMap[std::string("gamma")].c_str()) ;
  if( configureMap.find(std::string("coef0")) != configureMap.end())
    _param.coef0 = atof(configureMap[std::string("coef0")].c_str()) ;
  if( configureMap.find(std::string("nu")) != configureMap.end())
    _param.nu = atof(configureMap[std::string("nu")].c_str()) ;
  if( configureMap.find(std::string("cache_size")) != configureMap.end())
    _param.cache_size = atof(configureMap[std::string("cache_size")].c_str()) ;
  if( configureMap.find(std::string("C")) != configureMap.end())
    _param.C = atof(configureMap[std::string("C")].c_str()) ;
  if( configureMap.find(std::string("eps")) != configureMap.end())
    _param.eps = atof(configureMap[std::string("eps")].c_str()) ;
  if( configureMap.find(std::string("p")) != configureMap.end())
    _param.p = atof(configureMap[std::string("p")].c_str()) ;
  if( configureMap.find(std::string("shrinking")) != configureMap.end())
    _param.shrinking = atoi(configureMap[std::string("skrinking")].c_str()) ;
  if( configureMap.find(std::string("probability")) != configureMap.end())
    _param.probability = atoi(configureMap[std::string("probability")].c_str()) ;
}
void SVMTrain::initContentConfigure(const char * _fileName, std::map<std::string,std::string>& _configureMap) 
{
  std::ifstream configureStream(_fileName) ;
  if( !configureStream.is_open() )
    {
      INFO1("there is no svm configure file, use the default parameters to train svm!");
      return ;
    }
  std::string key ;
  std::string value ;
  char buf[1024] ;
  
  while( configureStream >> key >> value )
    {
      if( _configureMap.find(key) != _configureMap.end() )
	WARNING("key is not unique!") ;
      else
	_configureMap.insert(make_pair(key,value)) ;
    }
  configureStream.close() ;
}

// 读取特征向量到svm的feature空间下
void SVMTrain::readFeature(const char * _positivePath, const char * _negetivePath) 
{
  std::vector<std::string> positiveList ;
  std::vector<std::string> negetiveList ;
  getFileFromPath(_positivePath,positiveList) ;
  getFileFromPath(_negetivePath,negetiveList) ;
  std::string buf ;
  prob.l = 0 ;
  long long elems = 0 ;
  for( int i = 0 ; i < positiveList.size() ; ++ i )
    {
      INFO2("positiveFile",positiveList[i]) ;
      std::string fileName(_positivePath);
      fileName += positiveList[i] ;
      std::ifstream fileInStream(fileName.c_str()) ;
      while( getline(fileInStream,buf) )
	{
	  if( buf[0] == '#' ) continue ;
	  prob.l ++ ;
	  int countsTab = 0 ;
	  for( int i = 0 ; i < buf.size() ; ++ i )
	    if( buf[i] == '\t' ) countsTab ++ ;
	  elems += countsTab + 1 ; // 每个样本结束以-1为标志
	}
    }
  for( int i = 0 ; i < negetiveList.size() ;++ i )
    {
      INFO2("negetiveFile",negetiveList[i]) ;
      std::string fileName(_negetivePath) ;
      fileName += negetiveList[i] ;
      std::ifstream fileInStream(fileName.c_str()) ;
      while( getline(fileInStream,buf) )
	{
	  if( buf[0] == '#' ) continue ;
	  prob.l ++ ;
	  int countsTab = 0 ;
	  for( int i = 0 ; i < buf.size() ; ++ i )
	    if( buf[i] == '\t' ) countsTab ++ ;
	  elems += countsTab + 1 ; // 每个样本结束以-1为标志
	}
    }
  prob.y = new double[prob.l] ;// 样本标签
  prob.x = new svm_node*[prob.l] ;// 各个样本的起始地址
  nodeSpace = new svm_node[elems] ; // 样本的实际存储空间
  INFO2("line:",prob.l) ;
  INFO2("elem:",elems) ;
  if( param.gamma == 0 ) param.gamma = static_cast<double>(prob.l) / static_cast<double>(elems) ;
  INFO2("gamma",param.gamma) ;
  int indexLine = 0 ;
  long long indexNode = 0 ;
  for( int i = 0 ; i < positiveList.size() ; ++ i )
    {
      std::string fileName(_positivePath) ;
      fileName += positiveList[i] ;
      std::ifstream fileInStream(fileName.c_str()) ;
      while( getline(fileInStream,buf) )
	{
	  if( buf[0] == '#' ) continue ;
	  prob.x[indexLine] = &nodeSpace[indexNode] ;
	  prob.y[indexLine] = 1.f ; // 正样本
	  std::istringstream ss(buf) ;
	  int index = 0 ;
	  while( ss >> nodeSpace[indexNode].value )
	    {
	      nodeSpace[indexNode].index = index++ ;
	      indexNode ++ ;
	    }
	  nodeSpace[indexNode++].index = -1 ; // 隔开各个样本
	  ++ indexLine ;
	}
    }
  for( int i = 0 ; i < negetiveList.size() ; ++ i )
    {
      std::string fileName(_negetivePath) ;
      fileName += negetiveList[i] ;
      std::ifstream fileInStream(fileName.c_str()) ;
      while( getline(fileInStream,buf) )
	{
	  if( buf[0] == '#' ) continue ;
	  prob.x[indexLine] = &nodeSpace[indexNode] ;
	  prob.y[indexLine] = -1.f ; // 负样本
	  std::istringstream ss(buf) ;
	  int index = 0 ;
	  while( ss >> nodeSpace[indexNode].value )
	    {
	      nodeSpace[indexNode].index = index ++ ;
	      indexNode ++ ;
	    }
	  nodeSpace[indexNode++].index = -1 ;
	  ++ indexLine ;
	}
    }
  
}
// 训练svm模型
void SVMTrain::train()
{
  readFeature(svmPositiveFeaturePath.c_str(),svmNegetiveFeaturePath.c_str()) ;
  model = svm_train(&prob,&param) ;
  //preprocessFeature(svmPositiveFeaturePath.c_str(),svmNegetiveFeaturePath.c_str()) ;
  //secondTrain() ;
  if( svm_save_model(svmModelFile.c_str(),model) )
    {
      ERROR("can't save model to file") ;
      exit(-1) ;
    }
}
void SVMTrain::preprocessFeature(const char * _positivePath, const char * _negetivePath)
{
  if( _positivePath == NULL )
    {
      ERROR("positive path is NULL") ;
      exit(-1) ;
    }
  if( _negetivePath == NULL )
    {
      ERROR("negetive path is NULL") ;
      exit(-1) ;
    }
  std::vector<std::string> positiveList ;
  std::vector<std::string> negetiveList ;
  getFileFromPath(_positivePath,positiveList) ;
  getFileFromPath(_negetivePath,negetiveList) ;
  std::string buf ;
  svm_node * svmNodePtr = NULL ;
  int countsTab = 0 ;
  int framesNum = 0 ;
  for( int i = 0 ; i < positiveList.size() ; ++ i )
    {
      std::string fileName(_positivePath);
      fileName += positiveList[i] ;
      std::ifstream fileInStream(fileName.c_str()) ;
      std::ofstream fileOutStream("temp") ;
      while( getline(fileInStream,buf) )
	{
	  if( buf[0] == '#' ) 
	    {
	      std::istringstream ss(buf) ;
	      char c ;
	      ss >> c >> framesNum ;
	      continue ;
	    }
	  if( countsTab == 0 ) 
	    {
	      for( int i = 0 ; i < buf.size() ; ++ i )
		if( buf[i] == '\t' ) countsTab ++ ;
	      svmNodePtr = new svm_node[countsTab+1] ;
	    }
	  int index = 0 ;
	  std::istringstream ss(buf) ;
	  while( ss >> svmNodePtr[index].value )
	    svmNodePtr[index].index = index ++ ;
	  svmNodePtr[index].index = -1 ;
	  double predict_label = svm_predict(model,svmNodePtr) ;
	  if( predict_label == 1.f ) 
	    {
	      fileOutStream << "# "<< framesNum << std::endl ;
	      fileOutStream << buf << std::endl ;
	    }
	}
      fileInStream.close() ;
      fileOutStream.close() ;
      std::string cpOrder("cp ./temp ") ;
      cpOrder += fileName ; 
      INFO2("cp",cpOrder) ;
      system(cpOrder.c_str()) ;
    }
  
  for( int i = 0 ; i < negetiveList.size() ; ++ i )
    {
      std::string fileName(_negetivePath) ;
      fileName += negetiveList[i] ;
      std::ifstream fileInStream(fileName.c_str()) ;
      std::ofstream fileOutStream("temp") ;
      int countsTab = 0 ;
      while( getline(fileInStream,buf) )
	{
	  if( buf[0] == '#' ) 
	    {
	      std::istringstream ss(buf) ;
	      char c ;
	      ss >> c >> framesNum ;
	      continue ;
	    }
	  int index = 0 ;
	  std::istringstream ss(buf) ;
	  while( ss >> svmNodePtr[index].value )
	    svmNodePtr[index].index = index ++ ;
	  svmNodePtr[index].index = -1 ;
	  double predict_label = svm_predict(model,svmNodePtr) ;
	  if( predict_label == -1.f ) 
	    {
	      fileOutStream << "# " << framesNum << std::endl ; 
	      fileOutStream << buf << std::endl ;
	    }
	}
      fileInStream.close() ;
      fileOutStream.close() ;
      std::string cpOrder("cp ./temp ") ;
      cpOrder += fileName ; 
      INFO2("cp",cpOrder) ;
      system(cpOrder.c_str()) ;
    }
  delete [] svmNodePtr ;
}
void SVMTrain::secondTrain()
{
  readFeature(svmPositiveFeaturePath.c_str(),svmNegetiveFeaturePath.c_str()) ;
  svm_free_and_destroy_model(&model) ; // 释放model内存
  model = svm_train(&prob,&param) ;
}
void SVMTrain::getFileFromPath(const char * _path , std::vector<std::string>& _fileVector)
{
  DIR * pDir ;
  struct dirent * pDirent ;
  _fileVector.clear() ;
  if( _path == NULL )
    {
      ERROR("file path is NULL!") ;
      exit(-1) ;
    }
  pDir = opendir(_path) ;
  while( (pDirent = readdir(pDir)) != NULL )
    {
      INFO2("file in path",pDirent->d_name) ; 
      if( !(pDirent->d_type & DT_DIR) )
	{
	  _fileVector.push_back(std::string(pDirent->d_name)) ;
	}
    }
  closedir(pDir) ;
}
 void SVMTrain::trainSvmModel()
 {
   train();
 }
